﻿namespace Oddo.Concept.Crm.Common.Communication.Caching
{
    using System;
    using System.Linq;
    using System.Net;
    using System.Net.Http;
    using System.Net.Http.Headers;
    using System.Runtime.Caching;
    using System.Threading;
    using System.Web.Http.Controllers;
    using System.Web.Http.Filters;

    public class WebApiOutputCacheAttribute : ActionFilterAttribute
    {
        private readonly int _timespan = 0;
        private readonly int _clientTimeSpan = 0;
        private readonly bool _anonymousOnly = false;
        private string _cachekey = string.Empty;

        private static readonly ObjectCache WebApiCache = MemoryCache.Default;

        /// <summary>
        /// Creates a new instance
        /// </summary>
        /// <param name="timespan">server side cache length in seconds</param>
        /// <param name="clientTimeSpan">client side cache length in seconds</param>
        /// <param name="anonymousOnly">Allow cache for anonymous call</param>
        public WebApiOutputCacheAttribute(int timespan, int clientTimeSpan, bool anonymousOnly)
        {
            _timespan = timespan;
            _clientTimeSpan = clientTimeSpan;
            _anonymousOnly = anonymousOnly;
        }


        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            if (actionContext == null) throw new ArgumentNullException("actionContext");
            if (!_isCachingTimeValid(_timespan, actionContext, _anonymousOnly)) return;

            _cachekey = string.Join(":", new string[] { actionContext.Request.RequestUri.PathAndQuery, actionContext.Request.Headers.Accept.FirstOrDefault().ToString() });
            if (!WebApiCache.Contains(_cachekey)) return;

            var val = WebApiCache.Get(_cachekey) as string;
            if (val == null) return;

            var contenttype = (MediaTypeHeaderValue)WebApiCache.Get(_cachekey + ":response-ct") ?? new MediaTypeHeaderValue(_cachekey.Split(':')[1]);
            actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.OK, new StringContent(val));
            actionContext.Response.Content = new StringContent(val);
            actionContext.Response.Content.Headers.ContentType = contenttype;
        }

        public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
        {
            if (!(WebApiCache.Contains(_cachekey)) && !string.IsNullOrWhiteSpace(_cachekey))
            {
                var body = actionExecutedContext.Response.Content.ReadAsStringAsync().Result;
                WebApiCache.Add(_cachekey, body, DateTime.Now.AddSeconds(_timespan));
                WebApiCache.Add(_cachekey + ":response-ct", actionExecutedContext.Response.Content.Headers.ContentType, DateTime.Now.AddSeconds(_timespan));
            }

            if (_isCachingTimeValid(_clientTimeSpan, actionExecutedContext.ActionContext, _anonymousOnly))
                actionExecutedContext.ActionContext.Response.Headers.CacheControl = GetClientCache();
        }


        private readonly Func<int, HttpActionContext, bool, bool> _isCachingTimeValid = (timespan, ac, anonymous) =>
        {
            if (timespan > 0)
            {
                if (anonymous)
                    if (Thread.CurrentPrincipal.Identity.IsAuthenticated)
                        return false;

                if (ac.Request.Method == HttpMethod.Get) return true;
            }

            return false;
        };


        private CacheControlHeaderValue GetClientCache()
        {
            return new CacheControlHeaderValue
            {
                MaxAge = TimeSpan.FromSeconds(_clientTimeSpan),
                MustRevalidate = true
            };
        }
         
    }
}